#include "video.h"
#include "defuns.h"
#include "defs.h"


inline static void cOut(unsigned short port, unsigned char value);
inline static unsigned char cIn(unsigned short port);
static void checkCursorPos(void);


static char * vidmem = (char *)P_VIDEO_START;
/*Formata default de los caracteres de la pantalla*/
static char _default_screenFormat = BLACK_BACK | WHITE_CHAR;
static char _complement_screenFormat = GREY_BACK | WHITE_CHAR;
extern unsigned int cursorPos;


static int currentBlink = 0;


void clearScreen()
{
	unsigned int i;
	cursorPos=INI_CURSOR;
	for(i = 0; i < (SCREEN_W*SCREEN_H*CHAR_SIZE); i+=2)
	{
		vidmem[i]=0x00;
		vidmem[i+1]= _default_screenFormat;
	};
	return;
}

void
setBackground(char format)
{
	_default_screenFormat = (_default_screenFormat & 0x0F) | format;
	return;
}

void
setCharFormat(char format)
{
	_default_screenFormat = (_default_screenFormat & 0xF0) | format;
	_complement_screenFormat = GREY_BACK | (_complement_screenFormat & 0xF0);
	return;
}

void 
setFormat(char format)
{
	_default_screenFormat = format;
	return;
}

/*Formato de pantalla aternativo, usado para mostrar el cursor*/
void 
setComplementFormat(char format)
{
	_complement_screenFormat = format;
	return;
}


void put(char c, char format, int x, int y)
{
	// Indice dentro de la memoria de video
	int index = (y*SCREEN_W + x) * CHAR_SIZE;

	// writing character
	vidmem[index] = c;

	// writing attribute
	vidmem[index + 1] = format;
	return;
}

void putChar(char c,  int x, int y)
{
	// Indice dentro de la memoria de video
	int index = (y*SCREEN_W + x) * CHAR_SIZE;

	// writing character
	vidmem[index] = c;
	
	return;
}

void write(const char *str,  int x, int y)
{
	// Indice dentro de la memoria de video
	int index = (y*SCREEN_W + x) * CHAR_SIZE;

	while ( *str )
	{
		vidmem[index] = *str;
		str++;
		index+=2;
	}
	return;
}

void
moveScreenOneLine(void)
{
	unsigned int i;
		
	for(i = INI_CURSOR; 
	i < (SCREEN_W*(SCREEN_H-1)*CHAR_SIZE); i+=2)
	{
		vidmem[i]=vidmem[i+SCREEN_W*CHAR_SIZE];
		//vidmem[i+1]= _default_screenFormat;
	};
	cleanLastLine();
	return;
}

void
cleanLastLine(void)
{
	unsigned int i;
		
	for(i = SCREEN_W*(SCREEN_H-1)*CHAR_SIZE; 
	i < (SCREEN_W*SCREEN_H*CHAR_SIZE); i+=2)
	{
		vidmem[i]=0x00;
		vidmem[i+1]= _default_screenFormat;
	};
	return;
}

void
blinkCursor(void)
{
	//vidmem[cursorPos] = ' ';
	currentBlink++;
	currentBlink = currentBlink%2;
	//vidmem[cursorPos+1] = (currentBlink == 0)?_default_screenFormat:_complement_screenFormat;
	vidmem[cursorPos] = (currentBlink == 0)?'_':' ';
	return;
}



void 
writeCursorAllScreen(char *str)
{
	while (*str)
		putCharCursorAllScreen(*str++);

	return;
}

void
putCharCursorAllScreen(char c)
{
	//Borrar el fondo del cursor
	vidmem[cursorPos+1] = _default_screenFormat;
	// writing character
	if (c == '\t')
		cursorPos += 8 * CHAR_SIZE;
	else if (c == '\n')
	{
		vidmem[cursorPos] = 0x00;
		cursorPos = ((cursorPos/(SCREEN_W * CHAR_SIZE)) + 1) * SCREEN_W * CHAR_SIZE;
	}
	else if (c == '\b')
	{
		cursorPos -= 2;
		vidmem[cursorPos] = 0x00;
		vidmem[cursorPos+1] = _default_screenFormat;
		vidmem[cursorPos+2] = 0x00;
	}
	else
	{
		vidmem[cursorPos] = c;
		cursorPos+=2;
	}
	checkCursorPos();

	return;	
}

void 
writeCursor(char *str)
{
	while (*str)
		putCharCursor(*str++);

	return;
}



void 
putCharCursor(char c)
{
	//Borrar el fondo del cursor
	vidmem[cursorPos+1] = _default_screenFormat;
	// writing character
	if (c == '\t')
	{
		cursorPos += 8 * CHAR_SIZE;
	}
	else if (c == '\n')
	{
		vidmem[cursorPos] = 0x00;
		cursorPos = U_HSCREEN_START + ((cursorPos/(SCREEN_W* CHAR_SIZE)) + 1) * SCREEN_W * CHAR_SIZE;
	}else if (c!='\b')
	{
		vidmem[cursorPos] = c;
		cursorPos+=2;
	}
	checkCursorPos();

	return;	
}

void
backSpaceCursor(void)
{
	if(cursorPos -2>INI_CURSOR)
	{
		cursorPos -= 2;
		vidmem[cursorPos] = 0x00;
		vidmem[cursorPos+1] = _default_screenFormat;
		vidmem[cursorPos+2] = 0x00;
	}
}

void
nextLine(void)
{
	//Borrar el fondo del cursor
	vidmem[cursorPos+1] = _default_screenFormat;
	vidmem[cursorPos] = 0x00;
	cursorPos= ((cursorPos/(SCREEN_W * CHAR_SIZE)) + 1)* SCREEN_W * CHAR_SIZE;
	checkCursorPos();
}


// typedef's para representar un BYTE, , D_WORD
/*Mover cursor*/
void move_cursor(int x, int y)
{
  word index = y*SCREEN_W + x;

  /*Acceder al controlador de VGA*/
  /*Cargo la primera parte de la posision del cursor*/
  cOut(0x3d4, 0x0d);
  cOut(0x3d5,index & 0x00ff);

  /*Cargo la segunda parte de la posision del cursor*/
  cOut(0x3d4, 0x0e);
  cOut(0x3d5, (index * 0xff00) >> 8);
}

inline static void cOut(unsigned short port, unsigned char value)
{
  asm volatile("outb %0,%1"::"a"(value), "Nd" (port));
}

inline static unsigned char cIn(unsigned short port)
{
  unsigned char ret=0;
  asm volatile ("inb %1,%0":"=a"(ret):"Nd"(port));
  return ret;
}

static void
checkCursorPos(void)
{
	if(cursorPos>=SCREEN_W*SCREEN_H*CHAR_SIZE)
	{
		cursorPos-=SCREEN_W*CHAR_SIZE; /*Le resto una linea*/
		moveScreenOneLine();	/*muevo una linea para arriba*/
	}
	return;
}
